package Analisador;

import Tokens.Token;
import Util.Inicializar;
import Util.ListaErro;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/**
 *
 * @author Danilo
 */
public class Sintatico {

    
    /**
     * Método principal do analisar sintático, responsável por analisar a sequencia de token
     * que vem do Lexico
     * @param Text Texto de entrada
     */
    public static void scanner(String Text) {
        Lexico.scanner(Text);
        sincronizacao = Inicializar.carregaSincronizacao();

        programa(new ArrayList(Arrays.asList("FimDeArquivo")));
    }

    private static void programa(ArrayList<String> s) {

        tokenAuxiliar = Lexico.proximoToken();
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("program")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("programa_program")));
            erro(arrayAuxiliar, "Erro: falta a palavra reservada 'program'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        if (tokenAuxiliar instanceof Tokens.Identificador) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("programa_ident")));
            erro(arrayAuxiliar, "Erro: falta um identificador", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        if (tokenAuxiliar.getCadeia().equals(";")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("programa_;")));
            erro(arrayAuxiliar, "Erro: falta o símbolo ';'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("programa_corpo")));
        corpo(arrayAuxiliar);
        arrayAuxiliar.clear();
        if (tokenAuxiliar.getCadeia().equals(".")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            erro(arraySincronizacao, "Erro: falta o símbolo '.'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
        }
    }

    private static void corpo(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpo_dc")));
        dc(arrayAuxiliar);
        arrayAuxiliar.clear();
        if (tokenAuxiliar.getCadeia().equals("begin")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpo_begin")));
            erro(arrayAuxiliar, "Erro: falta a palavra reservada 'begin'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpo_comandos")));
        comandos(arrayAuxiliar);
        arrayAuxiliar.clear();
        if (tokenAuxiliar.getCadeia().equals("end")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            erro(arraySincronizacao, "Erro: falta a palavra reservada 'end'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
        }
    }

    private static void dc(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dc_dcV")));
        dcV(arraySincronizacao);
        arrayAuxiliar.clear();
        dc_p(arraySincronizacao);
    }

    private static void dcV(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("var")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcV_variaveis")));
            variaveis(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(":")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcV_:")));
                erro(arrayAuxiliar, "Erro: falta Símbolo ':'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcV_tipoVar")));
            tipoVar(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(";")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcV_;")));
                erro(arrayAuxiliar, "Erro: falta Símbolo ';'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            dcV(arraySincronizacao);
        }
    }

    private static void tipoVar(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        switch (tokenAuxiliar.getCadeia()) {
            case "real":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "integer":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            default:
                erro(arraySincronizacao, "Erro: falto o tipo ", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                break;
        }
    }

    private static void variaveis(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar instanceof Tokens.Identificador) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("variaveis_ident")));
            erro(arrayAuxiliar, "Erro: falta Identificador", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        maisVar(arraySincronizacao);
    }

    private static void maisVar(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals(",")) {
            tokenAuxiliar = Lexico.proximoToken();
            variaveis(arraySincronizacao);
        }
    }

    private static void dc_p(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("procedure")) {
            tokenAuxiliar = Lexico.proximoToken();
            if (tokenAuxiliar instanceof Tokens.Identificador) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcP_ident")));
                erro(arrayAuxiliar, "Erro: Falta indentificaro ", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcP_parametros")));
            parametros(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(";")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcP_;")));
                erro(arrayAuxiliar, "Erro: falta Símbolo ';'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("dcP_corpoP")));
            corpoP(arrayAuxiliar);
            arrayAuxiliar.clear();
            dc_p(arraySincronizacao);
        }
    }

    private static void parametros(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("(")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("parametros_listaPar")));
            listaPar(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(")")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: falta Símbolo ')'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        }
    }

    private static void listaPar(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("listaPar_variaveis")));
        variaveis(arraySincronizacao);
        arrayAuxiliar.clear();
        
        if (tokenAuxiliar.getCadeia().equals(":")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("listaPar_:")));
            erro(arrayAuxiliar, "Erro: falta Símbolo ':'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("listaPar_tipoVar")));
        tipoVar(arrayAuxiliar);
        arrayAuxiliar.clear();
        maisPar(arraySincronizacao);
    }

    private static void maisPar(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals(";")) {
            tokenAuxiliar = Lexico.proximoToken();
            listaPar(arraySincronizacao);
        }
    }

    private static void corpoP(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);
        
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpoP_dcLoc")));
        dcLoc(arraySincronizacao);
        arrayAuxiliar.clear();
        if (tokenAuxiliar.getCadeia().equals("begin")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpoP_begin")));
            erro(arrayAuxiliar, "Erro: falta a palavra reservada 'begin'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpoP_comandos")));
        comandos(arrayAuxiliar);
        arrayAuxiliar.clear();
        if (tokenAuxiliar.getCadeia().equals("end")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("corpoP_end")));
            erro(arrayAuxiliar, "Erro: falta a palavra reservada 'end'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        if (tokenAuxiliar.getCadeia().equals(";")) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            erro(arraySincronizacao, "Erro: falta o Símbolo ';'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());            
        }
    }

    private static void dcLoc(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        dcV(arraySincronizacao);
    }

    private static void listaArg(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("(")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll( mergeAuxArray(arraySincronizacao, sincronizacao.get("listaArg_(")));
            argumentos(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(")")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: falta Símbolo ')'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        }
    }

    private static void argumentos(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar instanceof Tokens.Identificador) {
            tokenAuxiliar = Lexico.proximoToken();
        } else {
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("argumentos_ident")));
            erro(arrayAuxiliar, "Erro: falta Identificador", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            arrayAuxiliar.clear();
        }
        maisIdent(arraySincronizacao);
    }

    private static void maisIdent(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals(";")) {
            tokenAuxiliar = Lexico.proximoToken();
            argumentos(arraySincronizacao);
        }
    }

    private static void pFalsa(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar.getCadeia().equals("else")) {
            tokenAuxiliar = Lexico.proximoToken();
            cmd(arraySincronizacao);
        }
    }

    private static void comandos(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar;
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar = Inicializar.pCmd();

        if (arrayAuxiliar.contains(tokenAuxiliar.getCadeia()) || arrayAuxiliar.contains(tokenAuxiliar.getTipo())) {
            arrayAuxiliar.clear();
            //tokenAuxiliar = Lexico.proximoToken();
            
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("comandos_cmd")));
            cmd(arrayAuxiliar);
            arrayAuxiliar.clear();
            
            if (tokenAuxiliar.getCadeia().equals(";")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("comandos_;")));
                erro(arrayAuxiliar, "Erro: falta Símbolo ';'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            comandos(arraySincronizacao);
        }
    }

    private static void cmd(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);
        //<editor-fold defaultstate="collapsed" desc="read"> 
        if (tokenAuxiliar.getCadeia().equals("read")) {
            tokenAuxiliar = Lexico.proximoToken();
            if (tokenAuxiliar.getCadeia().equals("(")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_read_(")));
                erro(arrayAuxiliar, "Erro: falta Símbolo '('", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_read_variaveis")));
            variaveis(s);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(")")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: falta Símbolo ')'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        } //</editor-fold>
        //<editor-fold defaultstate="collapsed" desc="write"> 
        else if (tokenAuxiliar.getCadeia().equals("write")) {
            tokenAuxiliar = Lexico.proximoToken();
            if (tokenAuxiliar.getCadeia().equals("(")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_write_(")));
                erro(arrayAuxiliar, "Erro: falta Símbolo '('", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_write_variaveis")));
            variaveis(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(")")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: falta Símbolo ')'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        } //</editor-fold>
        //<editor-fold defaultstate="collapsed" desc="while"> 
        else if (tokenAuxiliar.getCadeia().equals("while")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_while_condicao")));
            condicao(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals("do")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_while_do")));
                erro(arrayAuxiliar, "Erro: falta palavra reservada 'do'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            cmd(arraySincronizacao);
        } //</editor-fold>
        //<editor-fold defaultstate="collapsed" desc="if"> 
        else if (tokenAuxiliar.getCadeia().equals("if")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_if_condicao")));
            condicao(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals("then")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_if_then")));
                erro(arrayAuxiliar, "Erro: falta palavra reservada 'then'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                arrayAuxiliar.clear();
            }
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_if_cmd")));
            cmd(arrayAuxiliar);
            arrayAuxiliar.clear();
            pFalsa(arraySincronizacao);
        } //</editor-fold>
        //<editor-fold defaultstate="collapsed" desc="ident"> 
        else if (tokenAuxiliar instanceof Tokens.Identificador) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar = Inicializar.pListaArg();
            if (tokenAuxiliar.getCadeia().equals(":=")) {
                arrayAuxiliar.clear();
                tokenAuxiliar = Lexico.proximoToken();
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_ident_expressao")));
                expressao(arrayAuxiliar);
                arrayAuxiliar.clear();

            } else if (arrayAuxiliar.contains(tokenAuxiliar.getCadeia())) {
                arrayAuxiliar.clear();
                arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_ident_listaArg")));
                listaArg(arrayAuxiliar);
                arrayAuxiliar.clear();
            } else {
                erro(arraySincronizacao, "Erro: Not a statement", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        } //</editor-fold>
        //<editor-fold defaultstate="collapsed" desc="begin"> 
        else if (tokenAuxiliar.getCadeia().equals("begin")) {
            tokenAuxiliar = Lexico.proximoToken();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("cmd_begin_comandos")));
            comandos(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals("end")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: falta palavra reservada 'end'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        } //</editor-fold>
        else {
            erro(arraySincronizacao, "Erro: esperado comando", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
        }
    }

    private static void condicao(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);        
        
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("condicao_expressao")));
        expressao(arraySincronizacao);
        arrayAuxiliar.clear();
        
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("condicao_relacao")));
        relacao(arrayAuxiliar);
        arrayAuxiliar.clear();
        
        expressao(arraySincronizacao);
    }

    private static void relacao(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        switch (tokenAuxiliar.getCadeia()) {
            case "=":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "<>":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case ">=":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "<=":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case ">":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "<":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            default:
                erro(arraySincronizacao, "Erro: esperado operador de relação", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                break;
        }
    }

    private static void expressao(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        
        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("expressao_termo")));
        termo(arrayAuxiliar);
        arrayAuxiliar.clear();
        outrosTermos(arraySincronizacao);
    }

    private static void opUn() {

        switch (tokenAuxiliar.getCadeia()) {
            case "+":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "-":
                tokenAuxiliar = Lexico.proximoToken();
                break;
        }
    }

    private static void outrosTermos(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar;
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar = Inicializar.pOpAd();
        if (arrayAuxiliar.contains(tokenAuxiliar.getCadeia())) {
            arrayAuxiliar.clear();
            
            arrayAuxiliar = mergeAuxArray(arraySincronizacao, sincronizacao.get("outrosTermos_opAd"));
            opAd(arrayAuxiliar);
            arrayAuxiliar.clear();
            
            arrayAuxiliar = mergeAuxArray(arraySincronizacao, sincronizacao.get("outrosTermos_termo"));
            termo(arrayAuxiliar);
            arrayAuxiliar.clear();
            
            outrosTermos(arraySincronizacao);
        }
    }

    private static void opAd(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        switch (tokenAuxiliar.getCadeia()) {
            case "+":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "-":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            default:
                erro(arraySincronizacao, "Erro: está faltando operador", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                break;
        }
    }

    private static void termo(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        opUn();

        arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("termo_fator")));
        fator(arrayAuxiliar);
        arrayAuxiliar.clear();

        maisFatores(arraySincronizacao);
    }

    private static void maisFatores(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        arrayAuxiliar = Inicializar.pOpMul();
        if (arrayAuxiliar.contains(tokenAuxiliar.getCadeia())) {
            arrayAuxiliar.clear();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("maisFatores_opMul")));
            opMul(arrayAuxiliar);
            arrayAuxiliar.clear();

            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("maisFatores_fator")));
            fator(arrayAuxiliar);
            arrayAuxiliar.clear();

            maisFatores(arrayAuxiliar);
        }
    }

    private static void opMul(ArrayList<String> s) {
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        switch (tokenAuxiliar.getCadeia()) {
            case "*":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            case "/":
                tokenAuxiliar = Lexico.proximoToken();
                break;
            default:
                erro(arraySincronizacao, "Erro: esperado operador", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
                break;
        }
    }

    private static void fator(ArrayList<String> s) {
        ArrayList<String> arrayAuxiliar = new ArrayList<>();
        ArrayList<String> arraySincronizacao = new ArrayList<>();
        arraySincronizacao.addAll(s);

        if (tokenAuxiliar instanceof Tokens.Identificador) {
            tokenAuxiliar = Lexico.proximoToken();
        } else if (tokenAuxiliar instanceof Tokens.NumeroInteiro) {
            tokenAuxiliar = Lexico.proximoToken();
        } else if (tokenAuxiliar instanceof Tokens.NumeroReal) {
            tokenAuxiliar = Lexico.proximoToken();
        } else if (tokenAuxiliar.getCadeia().equals("(")) {
            tokenAuxiliar.getCadeia();
            arrayAuxiliar.addAll(mergeAuxArray(arraySincronizacao, sincronizacao.get("fator_(")));
            expressao(arrayAuxiliar);
            arrayAuxiliar.clear();
            if (tokenAuxiliar.getCadeia().equals(")")) {
                tokenAuxiliar = Lexico.proximoToken();
            } else {
                erro(arraySincronizacao, "Erro: Esperado Símbolo ')'", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
            }
        } else {
            erro(arraySincronizacao, "Erro: Esperado um fator", tokenAuxiliar.getLinha(), tokenAuxiliar.getColuna());
        }
    }

    /**
     * Método responsável por sincronizar o token com que se espera no grafo sintático.
     * @param array
     * @param erro Texto do erro
     * @param linha linha que está o token
     * @param coluna coluna que está o token
     */
    private static void erro(ArrayList<String> array, String erro, Integer linha, Integer coluna) {

        ListaErro.getInstance();
        String ErroFinal = erro + " Linha:" + linha.toString() + " Coluna: " + coluna.toString();
        ListaErro.addLista("Sintático", ErroFinal);

        while (!array.contains(tokenAuxiliar.getCadeia()) && !array.contains(tokenAuxiliar.getTipo())) {
            tokenAuxiliar = Lexico.proximoToken();
        }
    }

    /**
     * Método responsável por unir dois array
     * @param arraySincronização array principal
     * @param auxMerge array secundário
     * @return Array unificado
     */
    private static ArrayList<String> mergeAuxArray(ArrayList<String> arraySincronização, ArrayList<String> auxMerge) {
        ArrayList<String> arrayFinal = new ArrayList<>();

        arrayFinal.addAll(arraySincronização);
        arrayFinal.addAll(auxMerge);

        return arrayFinal;
    }

    //Declaração de variaveis
    private static HashMap<String, ArrayList<String>> sincronizacao;
    private static Token tokenAuxiliar;

}
